import abc
import functools
from typing import List, TypeVar, Generic, Union, Any

from pywebio.io_ctrl import Output
from pywebio.output import OutputPosition

from emi.model import PageResult,default_page_result
from emi.model.loader import DataLoader, SortItem
from emi.util import AsyUtil
from emiui import UISession
import pywebio
import pywebio_battery
from pywebio import pin, output

ItemType = TypeVar("ItemType")
DataType = TypeVar("DataType")


class DataLoaderView(Generic[ItemType,DataType]):

    """
    与DataListView不同，这里是包含排序功能的。
    """
    def __init__(self,
                 name: str,
                 datas: Union[List[ItemType],DataLoader[Any,ItemType]],
                 sort_items: List[SortItem[ItemType]] = None,  ## 排序方式
                 ui_session: UISession = None,
                 page_size:int = 50
                 ):
        self.PAGE_SIZE = page_size
        self.NAME = name
        self.PREFIX = f"{name}_dloadView"
        self.ui_session = ui_session
        self._sort_item_index = -1
        self._dialog_mode = False  ## 是否dialgo显示模式
        self._sort_items =  sort_items if sort_items else []
        if isinstance(datas,DataLoader):
            self.data_loader = datas
            self.data_loader.reset()
        elif isinstance(datas,list):
            self.data_loader = DataLoader.make(datas)
        else:
            raise NotImplementedError()
        self._on_init()

    def _on_init(self):
        pass

    def set_sort_items(self,sort_items: List[SortItem[ItemType]]):
        self._sort_items = sort_items


    @abc.abstractmethod
    def get_item_view(self,offset:int,is_head:bool,data:DataType):
        """
        获取列表元素。
        :param offset:  序号
        :param is_head:  是否列表头部
        :param data:
        :return:
        """
        pass

    def get_sort_items(self)->List[SortItem[ItemType]]:
        """
        返回支持的排序项
        :return:
        """
        return self._sort_items

    def load_item_data(self,items:List[ItemType])->List[DataType]:
        """
        加载item信息的详情数据 (在后台线程执行）
        :param items:
        :return:
        """
        return items


    async def show(self,is_show_header:bool = True):
        if self.ui_session is None:
            self.ui_session = UISession.get()
        if self._dialog_mode:
            output.put_scrollable([
                output.put_scope(name=f"{self.PREFIX}_main")
            ])
        else:
            output.put_scope(name=f"{self.PREFIX}_main")
        self._IS_SHOW_HEADER = is_show_header
        await self.refresh()


    async def refresh(self):

        sort_items = self.get_sort_items()

        if len(sort_items) > 0:
            if self._sort_item_index < 0:
                self._sort_item_index = 0
            self.data_loader.set_sort_item(1,sort_items[self._sort_item_index])
        item_list = self.data_loader.load()
        page_result = default_page_result(item_list,page_size=self.PAGE_SIZE)
        self.page_result = page_result
        with output.use_scope(name=f"{self.PREFIX}_main",clear=True):

            if self._IS_SHOW_HEADER:
                _option_data_ = []
                selected_index = page_result.cur_page()
                for page_index in range(1, page_result.total_page_count() + 1):
                    _option_data_.append({
                        "label": f"第{page_index}页",
                        "value": page_index,
                        "selected": selected_index == page_index
                    }
                    )

                async def __refresh_data():
                    page_index = await pin.pin[f"{self.PREFIX}_pages"]
                    if page_index is not None:
                        await self._load_page_at(page_index)
                row_items = []
                size_desc = ""

                if isinstance(self.data_loader, DataLoader):
                    option_items = self.data_loader.get_option_items()
                    async def _on_op_value_changed(option_name: str, new_value):
                        self.data_loader.set_option_value(option_name, new_value)
                        await self.refresh()
                    for option_item in option_items:
                        row_items.append(
                            pin.put_select(f"{self.PREFIX}_op_item_{option_item.name}", options=option_item.datas, help_text=option_item.label).style("margin-right:10px"))
                        pin.pin_on_change(f"{self.PREFIX}_op_item_{option_item.name}", onchange=functools.partial(_on_op_value_changed,option_item.name), clear=True)
                        size_desc += "180px "

                if sort_items:
                    sort_options = []
                    def _on_sort_changed(sort_index):
                        self._sort_item_index = sort_index
                        self.ui_session.post(self.refresh())

                    for index, sort_item in enumerate(sort_items):
                        sort_options.append({
                            "label": sort_item.name,
                            "value": index,
                            "selected": index == self._sort_item_index
                        })
                    row_items.append(pin.put_select(f"{self.PREFIX}_op_sort", options=sort_options,help_text="排序方式").style("margin-right:10px"))
                    size_desc += "150px "
                    pin.pin_on_change(f"{self.PREFIX}_op_sort", onchange=_on_sort_changed, clear=True)

                row_items.extend([
                    output.put_button(label="刷新", onclick=__refresh_data, small=True).style("margin-top:3px"),
                    pin.put_select(f"{self.PREFIX}_pages", options=_option_data_).style("margin-left:10px"),
                    output.put_scope(f"{self.PREFIX}_pages_desc"),
                ])
                size_desc += '70px 100px auto'
                output.put_row(row_items, size=size_desc)
                pin.pin_on_change(f"{self.PREFIX}_pages", onchange=self._load_page_at,clear=True)

            output.put_scope(f"{self.PREFIX}_result")

            if page_result.total_page_count() > 0:
                await self._load_page_at(1)


    async def _load_page_at(self,index):
        if self._IS_SHOW_HEADER:
            with output.use_scope(f"{self.PREFIX}_pages_desc", clear=True):
                pass
        with output.use_scope(f"{self.PREFIX}_result",clear=True):
            output.put_loading()

        async def _on_load_finished(data_list:List[DataType]):
            if self._IS_SHOW_HEADER:
                with output.use_scope(f"{self.PREFIX}_pages_desc", clear=True):
                    output.put_text(f"共有{self.page_result.total_count()}个数据，"
                                    f"当前显示页面: {self.page_result.cur_page()}/{self.page_result.total_page_count()}",
                                    position=OutputPosition.BOTTOM).style("margin-top:10px;margin-left:10px")

            with output.use_scope(f"{self.PREFIX}_result", clear=True):
                table_data = []
                table_data.append(self.get_item_view(0,is_head=True, data = None))
                for index, data in enumerate(data_list):
                    offset = (self.page_result.cur_page() - 1) * self.page_result.page_size() + index + 1
                    cell_list = self.get_item_view(offset,is_head=False, data = data)
                    if isinstance(cell_list[0], list):
                        table_data.extend(cell_list)
                    else:
                        table_data.append(cell_list)
                output.put_table(table_data)

        def _load_page_data():
            self.page_result.turn_page(index) ### 跳转页面或者刷新
            items:List[ItemType] = self.page_result.get_data()
            data_list:List[DataType] = self.load_item_data(items)
            self.ui_session.post(_on_load_finished(data_list))

        AsyUtil.run_in_thread(_load_page_data)


